/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    base64.c
 *  @brief   Base64 decode source file.
 *  @version v1.0
 *  @date    03. Apr. 2023
 ****************************************************************/

#include "base64.h"

/* Base64 decoding table */
static const u8 dec_table[128] = {
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
	0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
	0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
	0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

/**
 * Base64 decoding algorithm
 * @input Base64-encoded string
 * @inlen Length of the encoded string
 * @output Resulting decoded data
 * @outlen Length of the decoded data
 * @return Error code
 **/
int base64_decode(const char *input, u32 inlen, void *output, u32 *outlen)
{
	int error = 0;
	u32 value;
	u32 c;
	u32 i;
	u32 j;
	u32 n;
	u32 padlen;
	u8 *p;

	if (!input || !outlen)
		return -1;

	p = (u8 *)output;
	j = 0;
	n = 0;
	value = 0;
	padlen = 0;

	for (i = 0; i < inlen && !error; i++) {
		/* Get current character */
		c = (u8)input[i];

		/* Check the value of the current character */
		if (c == '\r' || c == '\n') {
			/* CR and LF characters should be ignored */
		} else if (c == '=') {
			/* Increment the number of pad characters */
			padlen++;
		} else if (c < 128 && dec_table[c] < 64 && padlen == 0) {
			/* Decode the current character */
			value = (value << 6) | dec_table[c];

			/* Divide the input stream into blocks of 4 characters */
			if (++j == 4) {
				/* Map each 4-character block to 3 bytes */
				if (p != NULL) {
					p[n] = (value >> 16) & 0xFF;
					p[n + 1] = (value >> 8) & 0xFF;
					p[n + 2] = value & 0xFF;
				}

				/* Adjust the length of the decoded data */
				n += 3;

				/* Decode next block */
				j = 0;
				value = 0;
			}
		} else {
			/* Implementations must reject the encoded data if it contains
			   characters outside the base alphabet (refer to RFC 4648,
			   section 3.3) */
			error = -1;
		}
	}

	if (!error) {
		/* Check the number of pad characters */
		if (padlen == 0 && j == 0) {
			/* No pad characters in this case */
		} else if (padlen == 1 && j == 3) {
			/* The "=" sequence indicates that the last block contains only 2 bytes */
			if (p != NULL) {
				/* Decode the last two bytes */
				p[n] = (value >> 10) & 0xFF;
				p[n + 1] = (value >> 2) & 0xFF;
			}

			/* Adjust the length of the decoded data */
			n += 2;
		} else if (padlen == 2 && j == 2) {
			/* The "==" sequence indicates that the last block contains only 1 byte */
			if (p != NULL) {
				/* Decode the last byte */
				p[n] = (value >> 4) & 0xFF;
			}

			/* Adjust the length of the decoded data */
			n++;
			/* Skip trailing pad characters */
			i++;
		} else {
			/* The length of the input string must be a multiple of 4 */
			error = -1;
		}
	}

	/* Total number of bytes that have been written */
	*outlen = n;
	return error;
}

